#include "chat.h"

int sockfd;
int addrlen;
struct sockaddr_in   server_addr; /*sockaddr_in在头文件#include<netinet/in.h>或#include <arpa/inet.h>中定义，
				    该结构体解决了sockaddr的缺陷，把port和addr 分开储存在两个变量中，
				    如下：
				     struct sockaddr_in{
        				unsigned short          sin_family;     
       				        unsigned short int      sin_port;
        				struct in_addr          sin_addr;
        				unsigned char           sin_zero[8];
									    }
					sin_family 一般为AF_INET（我们一般用因特网），2字节；
					sin_port是要监听的端口号，2字节；
					sin_addr一般设置为INADDR_ANY，表示可以跟任何（any）地址通信，4字节；
					sin_zero[8]，用来填充，8字节，Padding to make structure the same size as SOCKADDR。
					一共16个字节。
					*/

pthread_t pid;

int login_f =  -1;
	
void *func(void *arg) //线程运行的起始地址
{
	FILE *fp = NULL;//定义文件
	char a[1]={'\n'};
	char b[]="收到系统信息:";
	int len;
	char buf[128]={0};
	struct protocol *msg;//通信结构体指针
	
	while(1)
	{
		if(login_f != 1)
		{
			continue;
		}	
		memset(buf,0,sizeof(buf));
		len = read(sockfd,buf,sizeof(buf));
		if(len<=0)
		{
			close(sockfd);
			return;
		}
		msg = (struct protocol *)buf;
		if((msg->state == ONLINEUSER_OK)&&(msg->cmd == ONLINEUSER))
		{
			printf("%s\t",msg->name);
			continue;
		}
		if((msg->state == ONLINEUSER_OVER)&&(msg->cmd == ONLINEUSER))
		{
			printf("\n");
			continue;
		}
		char *filename = "test2.txt";//写入文件操作
		fp = fopen(filename, "a+"); 
       		int i;
		for (i = 0; i < strlen(b); i++)
		{
		fputc(b[i], fp);
		}
		fprintf(fp,"%s",buf);  //字符串使用%s
		fputc(a[0], fp);
		fclose(fp);//关闭文件
		buf[len]='\0';
		printf("%s\n",buf);		
	}	
}
void broadcast(int fd)
{
	struct protocol msg;
	FILE *fp = NULL;//定义文件
	char a[1]={'\n'};
	char b[]="本机发送的信息为:";
	msg.cmd = BROADCAST; //定义命令发送为公聊
	printf("发送消息:\n#");
	scanf("%s",msg.data);
	char *filename = "test1.txt";//写入文件操作
	fp = fopen(filename, "a+");        
	int i;
	for (i = 0; i < strlen(b); i++)
	{
		fputc(b[i], fp);
	}

	fprintf(fp,"%s",msg.data);  //字符串使用%s
	fputc(a[0], fp);
	fclose(fp);//关闭文件
	write(fd,&msg,sizeof(msg));
}
void private(int fd)
{
	struct protocol msg;
	FILE *fp = NULL;//定义文件
	char a[1]={'\n'};
	char d[1]={';'};
	char b[]="本机发送的信息为:";
	char c[]="发送对象为:";
	msg.cmd = PRIVATE;
	printf("输入想聊天的对象:\n#");	
	scanf("%s",msg.name);
	printf("发送消息:\n#");
	scanf("%s",msg.data);
	//strcpy(c,msg.data);
	char *filename = "test3.txt";//写入文件操作
	fp = fopen(filename, "a+");        
	int i,j;
	for (i = 0; i < strlen(b); i++)
	{
		fputc(b[i], fp);
	}
	fprintf(fp,"%s",msg.data);  //字符串使用%s
	fputc(d[0], fp);
	for (j = 0; j < strlen(c); j++)
	{
		fputc(c[j], fp);
	}
	fprintf(fp,"%s",msg.name);  //字符串使用%s
	fputc(a[0], fp);
	fclose(fp);//关闭文件
	write(fd,&msg,sizeof(msg));
}
void list_online_user(sockfd)
{
	struct protocol msg;
	
	msg.cmd = ONLINEUSER;
	
	write(sockfd,&msg,sizeof(msg));
	getchar();
	getchar();
	/*
	while(1)
	{
		read(sockfd ,&msg,sizeof(msg));
		if(msg.state == ONLINEUSER_OK)
		{
			printf("%s\t",msg.name);
		}else{
			break;
		}
	}*/
	
}
int  registe(int fd)
{
	struct protocol msg,msgback;//定义两个通信结构体，一个发送，一个返回

	msg.cmd = REGISTE;	//chat.h:#define REGISTE 0X00000004//注册账号
	printf("input your name\n");	//输入帐号名
	scanf("%s",msg.name);
	printf("input your passwd\n");	//输入密码
	scanf("%s",msg.data);

	write(sockfd,&msg,sizeof(msg));		 /*
						 ssize_t write(int fd,const void*buf,size_t count);
						 参数说明：
 						 fd:是文件描述符（write所对应的是写，即就是1）
  						 buf:通常是一个字符串，需要写入的字符串
 					 	 count：是每次写入的字节数
								*/
	read(sockfd,&msgback,sizeof(msgback)); /*
						用于从文件描述符对应的文件读取数据（从打开的设备或文件中读取数据）
						ssize_t read(int fd,void*buf,size_t count)
						参数说明：
						fd:      是文件描述符
						buf:     为读出数据的缓冲区；
						count:   为每次读取的字节数（是请求读取的字节数，读上来的数据保
         					存在缓冲区buf中，同时文件的当前读写位置向后移）

								*/
	if(msgback.state != OP_OK)    //操作未成功
	{
		printf("Name had exist,try again!\n");	
		getchar();
		getchar();
		return -1;
	}else{
		printf("Regist success!\n");//操作成功
		getchar();
		getchar();
		return 0  ;
	}
}
int login(int fd)
{
	struct protocol msg,msgback;

	msg.cmd = LOGIN;	
	printf("input your name\n");	
	scanf("%s",msg.name);
	printf("input your passwd\n");	
	scanf("%s",msg.data);

	write(sockfd,&msg,sizeof(msg));
	read(sockfd,&msgback,sizeof(msgback));
	if(msgback.state != OP_OK) //操作失败
	{
		printf("Name had exist,try again!\n");
		getchar();
		getchar();
		login_f = -1;
		return NAME_PWD_NMATCH; //返回错误信息
	}else{
		printf("Login success!\n");//操作成功
		getchar();
		getchar();
		login_f = 1;
		return OP_OK  ;
	}
}
int logout(int fd)
{
	close(fd);
	login_f = -1;
}
int main(int argc, char **argv)
{
	int sel;
	int ret; 
	int min_sel,max_sel;
	int portnumber;
	
	struct protocol msg; //定义通信结构体
	
	
	if(argc<3)    //如果运行程序的信号小于3,则报错
	{
		printf("cmd: %s ip portnumber\n",argv[0]);
		return;
	}
	//argv2 存放的是端口号 ，读取该端口，转换成整型变量
	if((portnumber=atoi(argv[2]))<0)
	{
		fprintf(stderr,"Usage:%s hostname portnumber\a\n",argv[0]); //端口号错误，报错
		exit(1);
	}
	sockfd = socket(PF_INET,SOCK_STREAM,0);	 /*	系统调用socket()来获得文件描述符
							int socket(int domain,int type,int protocol);
							第一个参数domain设置为“AF_INET”。
							第二个参数是套接口的类型：SOCK_STREAM或
							SOCK_DGRAM。第三个参数设置为0。
							系统调用socket()只返回一个套接口描述符，如果出错，则返回-1*/        									
	if(sockfd<0)
	{
		perror("socket() fail\n");  //socket创建失败
		return;
	}
	
	server_addr.sin_family =  PF_INET;	//使用sockaddr_in型结构体，sin_family 一般为AF_INET（我们一般用因特网），2字节，但是在unix/linux系统中有所区别，对于BSD,是AF,对于POSIX是PF.
	server_addr.sin_port   =  htons(portnumber);//初始化，sin_port是要监听的端口号，2字节；
	server_addr.sin_addr.s_addr   =  inet_addr(argv[1]);//初始化ip地址,获取想要连接的ip地址;
	
	addrlen = sizeof(struct sockaddr_in);//计算sockaddr_in的大小
	
	connect(sockfd,(struct sockaddr* )&server_addr,addrlen);/*connect()用于建立与指定socket的连接。int connect(int sockfd,struct sockaddr* serv_addr,int addrlen);
								第一个参数还是套接口文件描述符，它是由系统调用socket()返回的。
								第二个参数是serv_addr是指向数据结构sockaddr的指针，其中包括目的端口和IP地址。
								第三个参数可以使用sizeof(structsockaddr)而获得。*/
	pthread_create(&pid, NULL,func, NULL);	         /*创建线程,pthread_create第一个参数为指向线程标识符的指针。
										  第二个参数用来设置线程属性。
										  第三个参数是线程运行函数的起始地址。
										  最后一个参数是运行函数的参数。*/
	while(1)
	{
		//getchar();
		system("clear");
		if(login_f == -1){     //login_f为登陆状态检测
			printf("\t 1 注册 \n");
			printf("\t 2 登陆\n");
		}else if(login_f == 1){ //登陆成功后
			printf("\t 3 公聊 \n");
			printf("\t 4 私聊 \n");
			printf("\t 5 在线人数\n");						
		}	
		printf("\t 0 exit\n");
		
		fflush(stdin);//清理标准输入流，把多余的仍未被保存的数据丢掉
		scanf("%d",&sel);
		if(sel == 0)
		{
			break;
		}
		if(login_f == 1) //控制已登陆用户选项
		{
			min_sel = 3; //选项为3~5
			max_sel = 5;
		}else if(login_f == -1){      //控制未登陆用户选项
			min_sel = 1;          //选项为1~2
			max_sel = 2;
		}
		if(sel<min_sel || sel > max_sel)
		{
			printf("选项错误，重新输入:\n");
			continue;
		}
		switch(sel)
		{
			case 1:
				registe(sockfd);   //注册功能实现
				break;
			case 2:
				ret = login(sockfd);   //登陆功能实现
				break;
			case 3:
				broadcast(sockfd);   //公聊功能实现
				break;
			case 4:
				private(sockfd);   //私聊功能实现
				break;
			case 5:
				list_online_user(sockfd);   //查看当前在线人数
				break;
			case 0:
				logout(sockfd);   //退出
				break;
			default:
				break;
		}
		if(sel == 0)
		{
			exit(0);
		}
	}
	
	

}
